﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Mammoth.Libs
{
    public enum ReflectionQueryType
    {
        Class, Abstrac, Interface
    }

    public static class ReflectionExtensionMethods
    {
        public static IEnumerable<Type> GetTypesByGenericInterface(this IEnumerable<Type> types, Type genericBaseClass, ReflectionQueryType type = ReflectionQueryType.Class)
        {
            Func<Type, bool> SearchByDefinition = y => y.IsGenericType &&
                            y.GetGenericTypeDefinition() == genericBaseClass;
            switch (type)
            {
                case ReflectionQueryType.Class:
                    return types.Where(x => x.GetInterfaces()
                        .Any(SearchByDefinition) & !x.IsAbstract & !x.IsInterface);
                case ReflectionQueryType.Abstrac:
                    return types.Where(x => x.GetInterfaces()
                        .Any(SearchByDefinition) & x.IsAbstract);
                case ReflectionQueryType.Interface:
                    return types.Where(x => x.GetInterfaces()
                        .Any(SearchByDefinition) & x.IsInterface);
                default:
                    return types.Where(x => x.GetInterfaces()
                        .Any(SearchByDefinition) & !x.IsAbstract & !x.IsInterface);
            }
        }

        public static IEnumerable<Type> GetTypesByBaseClass<T>(this IEnumerable<Type> types, ReflectionQueryType type = ReflectionQueryType.Class)
        {
            return GetTypesByBaseClass(types, typeof(T), type);
        }

        public static IEnumerable<Type> GetTypesByBaseClass(this IEnumerable<Type> types, Type baseType, ReflectionQueryType type = ReflectionQueryType.Class)
        {
            switch (type)
            {
                case ReflectionQueryType.Class:
                    return types.Where(x => baseType.IsAssignableFrom(x) & 
                        !x.IsAbstract & !x.IsInterface);
                case ReflectionQueryType.Abstrac:
                    return types.Where(x => baseType.IsAssignableFrom(x) & 
                        x.IsAbstract);
                case ReflectionQueryType.Interface:
                    return types.Where(x => baseType.IsAssignableFrom(x) & 
                        x.IsInterface);
                default:
                    return types.Where(x => baseType.IsAssignableFrom(x) & 
                        !x.IsAbstract & !x.IsInterface);
            }

        }

    }
}
